Lesson 11: Printer Friendly

Creating a Homework Quiz Project

Printing This Lesson

Select what you’d like to include when you print, and then click the Print Lesson button:

Saving This Lesson

For instructions on saving this lesson (shown below), please select the browser you're using.

chrome icon
Chrome
Firefox icon
Firefox
Internet Explorer 10 icon
IE 11
Safari icon
Safari

Lesson 11 - Creating a Homework Quiz Project - Chapter 1

Introduction

Wow. We're already on Lesson 11. Time flies when you're having fun!

Today we're going to create two VB programs. The first program, Quiz Data Entry, transforms user data entry into an XML file, and the second program, Quiz, uses this XML data to create a quiz for the user. This would come in handy for students or anyone studying for a test.

You'll also learn a good way to design new computer programs by first listing the program's tasks and then creating an interface based on those tasks. Only after that do you write and test the code.

And you'll practice using various programming techniques, including initialization, looping, validation, and providing user feedback. When you're finished with this lesson, you'll have built two practical programs that, together, can quiz anyone on any topic!

Chapter 2

Sketching in a Program

Let's start by writing a program that makes it easy for the user to type in questions and answers. Many programmers create a new program by following these five steps:




We'll begin by describing the jobs that our data-entry program must do:

  1. Create an XML file to hold the quiz data the user will type in.
  2. Display a textbox for the user to enter each quiz question, and a second textbox to type in each answer (true or false).
  3. Detect when the user has pressed the ENTER key. This is how the user tells the program that he or she has finished typing in the question/answer pair (let's call it the Q/A pair from now on).

    At this point, our program should translate the Q/A pair into an XML element and append it to the XML document. We can put this code into the Form1_KeyDown event (that event checks all keypresses, no matter in which control the user is typing at the time).
  4. Add a label control for each textbox, explaining what to type into them. Add another label saying the user should press the ENTER key after completing each Q/A pair.
  5. Empty the textboxes so they're ready for the data entry person to type in the next Q/A pair. This is the equivalent of "turning the page" to see a new, blank form.
  6. Add an Exit button that, when clicked, ends the program.

Think you got it?



XML Challenge!!


See if you can figure out what events this data-entry program needs to do its jobs. In other words, for each of the numbered items in the above list, what code must you write, and where should you put that code?

For example, you should put item 6's code in the Exit button's Click event. And remember, some controls on the form, such as the labels, require no code.





Okay, now double-click the Quiz Data Entry.sln file in the C:\XML L11 Finished\Quiz Data Entry first draft folder to open it in VS. As always, remember to double-click form1.vb in the Solution Explorer to view the form, and then double-click the form to see the code.

Simple data entry form

Now it's time to create the code.

Creating and Examining the Code

When creating a new XML document, all you need is that declaration code on the first line and the root element on the second line. In this document, we've named that <quiz>.

Here's the code I came up with for the initialization—the preliminary housekeeping—for the data entry program:

Let's take a closer look at this code. Watch this video as I walk you through it.


Chapter 1, Video 1: "Examining the Quiz Program's Initialization Code", TRANSCRIPT

Let's take a look at the initialization code, the things that the program does before turning control over to the user. These first two Dim statements here in lines 9 and 10 are identical to the code that begins the Cookbook program. They create global variables, variables that can be accessed by any of the events in the form. Line 9 specifies the file path, the location of the XML document on the hard drive. And Line 10 declares an XML document object that we're going to call doc, and it will hold the entire quiz data from the file on the hard drive, so we can work with it while the program runs.

Then we have an If, End If block of code. This checks to see whether this file, quiz.xml, already exists on the hard drive. If it doesn't exist, then the file is created here in this block of code using VB's StreamWriter command. VB has a dozen ways to create, read, and write files. You can create a new XML file, for example, using more complex XML techniques, but an XML file is just plain, vanilla text. And the VB StreamWriter works quite well for this task.

Up top here we use the Imports command to bring in the System.IO library. It's where the StreamWriter is located, and that's something makes it easier for us to write code. We don't have to add System.IO in front of every SW variable, wherever it might appear. Here in line 17, I name the StreamWriter SW, and then in line 18, I create the object, and I also give it the file path. So SW is now associated with that location on the hard drive. And line 20 puts in the necessary declaration line of code into the file, and then line 21 puts in the root, quiz\quiz, the root for the entire file. And here in line 22, we close SW, which has the effect of saving all this to the hard drive.

We simplified this program, basically, by not asking the user: where do you want to put the file in the hard drive, and are you starting a new quiz, or are you adding some new elements to an existing quiz? All of that just sort of happens in the program. The user will make the quiz longer by appending questions if the file exists on the hard drive. If it doesn't exist, the user will start a new program. So the only way the user can start a brand-new quiz is to rename quiz.xml on the hard drive or delete it so that the program won't find it, and then the program will start a new quiz file.

Line 26 here creates the new XML document object, and then line 27 loads the contents of the XML file from the hard drive into this doc object so we can use it. Remember that doc is global. This means that from now on the program can access the XML file data that we loaded into it right here on the initialization, but anywhere else in the program, we can now call upon doc.

Then, finally, in line 29 here, we set the focus to the txtQuestion textbox so that when the user starts typing, that's where the text will appear. And line 31 sets the form's KeyPreview property to true. Now, this property, which is false by default, makes the program watch every pressed key because we're setting it to true. We want our program to be able to know when the user presses the Enter key, signaling that they've finished typing in a QA pair and want to move to the next blank form to start writing another QA pair.

END TRANSCRIPT

Saving an XML Element

All that's left to do in this program is to save a QA element to the XML file when the user presses the ENTER key. The form's KeyDown event watches for the ENTER key:

If the user presses the ENTER key, then everything between lines 2-20 is executed. This code might seem familiar to you—it's quite similar to code we wrote in the btnImport_Click event in the cookbook program.

  • Lines 3-5 create three elements for use in the doc XML code: a parent (qapair) and two children (question and answer).
  • In lines 7-8, the text the user typed into the two textboxes is saved to the InnerText (the content) of the two XML child elements.
  • Lines 10-11 add the child elements to their parent element.
  • Line 13 appends the qapair parent element (including its children) to the document.
  • Line 15 overwrites the existing quiz.xml file, updating it.

Then, we prepare the UI so the user can type in the next question by emptying the textboxes and setting the focus on the txtQuestion textbox.

The data entry program is now working, but it's only a first draft. Put another way . . . it still has some flaws.


Let's Chat!

In the next chapter, we'll make several improvements to the data entry program—enhancing its efficiency and ensuring that correct data is entered (validating). But how many rough edges can you find in the data entry program at this point? Make a list of improvements you'd like to see, and then compare notes with your classmates in the Discussion Area.






Chapter 3

Polishing the First Draft

Your first attempt at writing a computer program larger than a few lines will always be flawed. Not just bugs, but also inefficiencies, redundancies, and things that might annoy, mislead, or trip up the user.

So you'll always want to put your program through its paces. Use it for a while, noting anything you should improve. And if you have a few willing friends, ask them to test it too.

Here are the six shortcomings that I found when trying out this first draft of the Quiz Data Entry program:




Now close VB. We're finished looking at the first draft of the data entry program.

In Windows File Explorer, open the C:\XML 11 Finished\Quiz Data Entry Improved Final folder. Click the Quiz Data Entry.sln file to reopen VB with the polished, final version of this program. Now you can follow along and view the modified, improved code where I've fixed the six shortcomings listed above.

Fixing Form_Load

When users start our program, they either want to begin creating a new quiz or append questions to an existing quiz. In Form_Load, we should explain how to accomplish these goals if a quiz.xml file already exists. So make the following highlighted changes to Form_Load:

Adding Data Validation

Now, our second fix is to validate the data. People enter typos, get tired or mixed up, or otherwise type in bad data. You don't want to mess up your XML file with empty elements, answers where there should be questions, and so on.

So you should assume that there will be errors during data entry. Have your program validate the data—and if it finds a problem, it should display a message box explaining what's wrong and how the user can fix it.

Let's get started adding some validation code. We need to fix two potential problem areas in our program:

  • Does the answer textbox contain only a t or an f? We can't permit any other input, because the Quiz program we'll write later in this lesson will be looking for a t or f answer only.
  • When the user presses the TAB key to move out of the answer textbox (to move to the next empty form), does the question textbox contain a question, or is it empty? If it's empty, we need to tell the user to type a question.

What event should we use to add our validation code? Not surprisingly, the best textbox event to use for validation is the Validating event! And we want to validate just before saving the Q/A pair and moving on to a new, blank form. This happens when the user has just finished typing into the txtAnswer textbox (and pressed the TAB key). So we'll put our validating code in the txtAnswer_Validating event.

  1. Double-click form1.vb in the Solution explorer.
  2. Now, double-click the form and locate the txtAnswer_Validating event in the Code window.

Most of this code is easy to understand. The user has just pressed the TAB key, triggering this Validating event. This trigger fires, because those who wrote VB understand data entry, so they know that validation should happen right after a TAB key is pressed.

We test to see if there's a t or f in the textbox—and if so, execute a sub called SaveQA (we'll add that sub in a minute). But if t or f is not in the textbox, the data is invalid and the e.cancel  command keeps the focus in the txtAnswer textbox (in other words, the TAB key is ignored, and we stay right where we were). Then we tell the user the problem and empty the txtAnswer textbox, so the user can type in the required t or f.

Now we need to create a new sub we'll call SaveQA. It will append the current Q/A pair to the Quiz.xml file (a job previously done by the KeyDown event). The reason for putting this code in a separate, new sub (rather than in an existing event) is that two different events will need to save the Q/A pair. So rather than duplicating this code in those two events, we'll just have the events execute in a new custom SaveQA sub we create.

  1. Locate Sub SaveQA in the code window.
I deleted the entire KeyDown event from the earlier version of this program. We won't use it anymore. KeyDown was where we checked to see if the user pressed the ENTER key. But from now on, we'll let VB trigger the txtAnswer textbox's Validating event when a TAB happens.

Validating That No Fields Are Empty

We also want to validate that when the user presses the TAB key both textboxes contain text. This is when our code saves the Q/A pair. By insisting on t or f, we've already ensured that the txtAnswser field has the right content. So now all we need to do is make sure that the txtQuestion field isn't empty. Recall that in VB "" (empty double quotes) means "no text."

I changed the first line in the txtAnswer_Validating event to add this new condition:

What we're saying here is that txtQuestion field must not be empty (<> means not-equal-to). So it says, "not equal to empty."

Did you notice that I put parentheses around the two Or conditions here? That tells VB to check this Or condition before checking the And condition.

Does it matter which conditions VB analyzes first? It certainly does. This complex condition can mean two entirely different things.

Consider this similar real-world, three-condition statement:


If you're driving in a terrible rain storm or you're feeling tired and you're in no hurry, then it's best to check into a motel.

This sentence can be interpreted two different ways:

If you're in a storm or tired, and also in no hurry, get a motel.

(storm or tired) and no hurry.



If you're in a storm, then get a motel.

storm or (tired and no hurry).



Enter Key Fixed for Free

Remember that one of our issues with this program was that we shouldn't use the ENTER key to move to a new, blank form?

During data entry, it's common practice to press the TAB key to move from one field to the next when entering data into a computer form. Why use a key? Because if you require the user to click the mouse to move to the next field, the user has to waste time taking his or her hand off the keyboard to click the next textbox (so the user can type into it). And you then have to waste some more time getting your hand back into the correct position on the keyboard after you've clicked the mouse. Do this keyboard-mouse-keyboard maneuver 20 times, and it gets old fast.

Our program already uses TAB to move from the Question textbox field to the Answer field. But TAB should also move the user from a completed form to the next, blank form. We didn't follow this data entry convention. Instead, we asked the user to press the ENTER key to move to the next form. Bad idea.

The TAB key should do all the moving: Question—TAB—answer—TAB—question—TAB—answer, and so on.

Luckily for us, we don't need to do anything to fix this problem. The changes we made to add validation also made the TAB key the only way for the user to move to the next form. So we got this change for free, as a side-effect of adding validation!



Avoiding Typing True or False

This fix is simple. In the first draft of the data entry program, we had a label next to the Answer textbox that told the user to type True or False (T or F). That's clumsy in two ways. Users shouldn't have to keep typing the whole words True and False. They should just type T and F. What's more, they don't need to capitalize these letters either. Stick with lowercase t and f. (Anything to make the data entry more efficient.)

How do we change this? Just change the label on the form to read: Type in t or f. (But as you'll see shortly, we'll get rid of this label completely and move these instructions up to the form's title bar.)

Ensure We're Saving All Elements

As it stands, our Exit button is dangerous. The only code in it is the End command. So what will happen if the user finishes typing in a question and answer, but then clicks the Exit button? The program will end without saving that final Q/A pair that the user just typed in.

Clearly this is a bug we need to fix. We need to put some code in the btnExit_Click event that checks to see if the two textboxes contain text. And if they do, we must save this final Q/A pair element to the quiz.xml file.

I added this code to the btnExit_Click event to accomplish these goals:

Translation: End the program if either of the text fields are blank. I figured that the user wouldn't accidentally click the Exit button. In other words, if either field is empty, the user wants to throw away this Q/A pair, so I end the program. But if neither field is empty, then save the Q/A pair before ending the program.

Fixing an Ugly UI

In the first draft of the data entry program we had another boring default gray interface. But you now know how to improve the UI on your own after learning how to use the BackgroundImage and other properties in previous lessons. In addition to adding some graphics to the form's background and exit button, I removed the labels and just put their instructions up in the form's title bar.

People generally fall into two camps when it comes to their design preference—clean or complicated. The technical terms for this difference in styles are modern or Baroque. The former is simplified and clean but can also be severe, and the latter is complex and ornate, although sometimes cluttered. Think of a modern square glass box building versus a Renaissance Italian church.



Both styles can be good, but I mostly lean toward Baroque, as you probably already suspect from the way I added all that gold texture to the cookbook program in a previous lesson. But choose the style you like.

Here's one of my favorite UI tricks: Do your background in a graphics program. They have hundreds of tools, textures, and other options:

  1. Take a screenshot of the UI (run your program, and then press ALT + PrtScn to capture the form).
  2. Paste the UI into a graphics editor like Photoshop.
  3. Crop off the form's frame, a title bar, so just the actual BackGroundImage is showing.
  4. Fill the background with textures, gradients, images, whatever you like.
  5. Add drop shadows around the buttons.
  6. Once you've finished making the UI look great, save the file, and import it into your program as the BackGroundImage of the form.

Subtle drop-shadows

Notice that I got rid of the labels and put their instructions into the form's Text property.

Great work! All right, take a break, and then meet me in Chapter 4 where we'll create a program that gives a quiz.

Chapter 4

Creating a Quiz

Now, let's look at the companion program to the Quiz Data Entry project. Called simply Quiz, this program consumes (programmer-talk for uses) the data created by the data entry program that was stored in the quiz.xml file. And that data is the quiz questions.

Go ahead and open the quiz program by double-clicking the quiz.sln file in your C:\XML L11 Finished\Quiz folder.


It's surprisingly easy to write this program. It requires very little code if we keep things simple. As always, start by listing the tasks the program needs to do:




Normally, the second step when writing a VB program is to build the UI—you add whatever controls are required to do the jobs listed in the first step.

But because we're keeping this program very uncomplicated, we won't even use controls. We won't display a form. We'll just ask the true/false quiz questions by displaying a series of message boxes.

So far in this course, we've worked with only the most basic type of message box—one that presents some text to the user, along with an OK button that dismisses the message box if the user clicks it. But message boxes come in several styles. For example, a message box can accept yes or no input from the user, like this:


Simple message box

Let's see how it works.

    As always, the housekeeping code goes into the Form_Load event, because that event automatically executes when the program first starts up.

    I started with the following lines of code at the top of the Code window. This programming should be very familiar to you by now.

    The Cookbook program uses this same code with the exception of the filepath and filename. To review, this is what's happening in this code:

    • You import the XML library so you can use commands like XmlDocument without having to prefix them with the XML namespace.
    • The Dim command when used like this outside any Sub creates a global variable—usable by any event in this program. Dim declares (names) variables, but these variables don't yet hold any content.
    • The Form_Load event begins by checking to see if the XML file exists on the hard drive. If it's missing, we alert the user and end the program.
    • The New command creates the doc object variable. (We call this act of creation instantiation in computerese.)
    • The Load command copies the quiz.xml file—both its XML structure and its content—into the doc object.
    • The Form_Load event has done its job and now turns the program execution over to a Sub I named RunTheQuiz.

    Building the Main Loop

    Many programs keep repeating something (looping) until some condition is satisfied. It's the same idea as real-world jobs like washing a car—you keep rubbing a soapy sponge on the car until it's clean.

    The loop part is the rubbing, and the exit condition is that it's clean. There might be smaller, secondary loops, like hosing water into the bucket until it's full, and wringing the sponge until it's not waterlogged. But clearly the main loop—the main action—is the rubbing.

    Similarly, the main loop for our quiz is asking questions over and over. And the exit condition is running out of questions, reaching the end of the XML document.

    But we want to use a larger loop, too, an outer loop that can repeat the entire quiz if the user wants to try again. And the outer loop's exit condition is the user clicking a No button, saying that they don't want to take the quiz again.


    Chapter 4, Video 1: "Building the Main Loop", TRANSCRIPT

    For those of you who are interested in how VB code works, let's break this down. We created a custom subroutine called RunTheQuiz. This custom sub starts off by declaring some variables. Now, these two variables here we've used in previous programming. The XMLNodeList collection will contain all the question elements in the quiz.xml document. And the node object will fetch each question and then its answer in turn as the loop goes through the entire document.

    And we'll use three string variables. Q holds each question one at a time during the quiz loop. A holds each answer. And T displays the current question number in the title bar of the message boxes that we'll display to the user.

    And we'll use five integer variables. Counter keeps track of which QA pair we're currently working with. In other words, is this the first QA, is it the second, or which one is it? We'll use this information when we display, on the title bar, question 3 of 5, so they can see where they are in the quiz. Or question 2 of 10 or whatever it is.

    Correct keeps track of how many questions the user's gotten right throughout the quiz. UserAnswer variable holds the information about which button on the message box the user has clicked. In VB's code, a 6 means yes and a 7 means no. That's just a code inside Visual Basic to tell you what the button was that the user clicked.

    Percent tells us the user's score. It's calculated by dividing the number of correct answers by the total number of answers in the quiz. So we use this to give feedback back to the user after the quiz is over. Based on this percent, we display one of seven messages to the user, ranging from "You didn't answer any questions correctly" to "You got a perfect score." And, finally, the integer variable TotalQuestions holds the number of questions in the quiz.

    Now, the action begins. The Do command, followed later on the—by the Loop and Tell command, defines the outer loop block, and we ask and answer the questions within the main loop, which is For Each right here. So we have two loops, an outer loop and a main loop.

    We start out by getting our ElementList here, which fills this Element List collection with all of the question elements in the file. GetElementByTagName "question." And then we fetch the total number of questions by using the Count command of the ElementList object. This tells us how many total questions there are.

    And now you can return to the main lesson, where, in the text, of the lesson I describe how these two loops work to complete the quiz.

    END TRANSCRIPT



    Tip

    Reset Your Variables

    It's a good idea to reset certain variables inside a loop. That's why we put 0 in three variables in this code. If we didn't do this, they could hold the wrong values each time the Do loop repeats (each time the user takes the quiz over).

    For example, the correct variable holds the number of questions the user got right. In a four-question quiz, it might hold three after the user takes the quiz the first time. But if the user takes the quiz a second time, this variable would accumulate more correct answers, perhaps adding another three. It would then hold six—two more than the total number of questions! Obviously, you need to reset the number in the correct variable to 0 each time the user repeats the quiz.

    Then, the code loops until we have asked all the questions (For Each…Next). The loop goes through the nodelist (we named it ElementList). The exit condition is that there are no more individual elements in the list left to process.

    The variable q gets a copy of the InnerText (the content) of the current <question> element. Each time through the loop, we raise the counter variable and use it to create our message: Question x of x.

    Then, we ask a question, using the vbYesNo messagebox style. We display the question like this:


    Answer Yes or No

    To visualize how the quiz program is fetching each question and then its answer, here's a sample quiz.xml document generated by the Quiz Data Entry program we created earlier in this lesson:

    The variable a gets a copy of the current <answer> element's data (t or f). We then see if the user answered correctly. (The LCase command ensures that the answer is lowercase. It's possible that the person who typed in the quiz was capitalizing the t's and f's.)

    How do we know if the user answered correctly? Good question!

    If the answer in the XML file is f, and the user clicked the No button (remember that 7 is VB's code number for No)—then the user got it right, so we increase (+=) by 1, the number of correct answers. Likewise, if the XML answer is t, and the user clicked the Yes button, that's also a right answer.

    Then the Next command causes the loop to repeat and ask the next question.

    Giving Feedback

    The program ends by telling the user how he or she did. We calculate the percent of correct answers:

    If correct > 0 Then percent = 100 * (correct / counter)

    Then, based on this percent, we display an appropriate message to the person taking the quiz:



    Decoder Game

    Chapter 5

    Summary

    Wow, what a lesson! Today you practiced some important programming techniques, including how to design programs from scratch by writing a list of tasks the program must carry out. You also learned one way to translate plain text user input into a structured XML data file—and then later consume this data by translating the XML into text and displaying it to the user. And you also explored how to construct a loop-driven program structure that quits when an exit condition is satisfied, rather than waiting until the user clicks an Exit button.

    Let's do a quick review of the tasks a quiz program needs to carry out. Can you complete these sentences?


    Drop Down Game

    Don't forget to check out the FAQs and Supplementary Material for this lesson, and be sure to complete the assignment and take the quiz. In our final lesson, you'll design and code a more complex project that manages an XML database containing elements with multiple children!.

    Supplementary Material

    http://en.wikipedia.org/wiki/Principle_of_least_astonishment

    FAQs

    Q: You said that when you chain together multiple conditions, it can matter which conditions are evaluated first. Can you explain this a little more?

    A: Sure! This does matter greatly, because complex conditional statements are by nature ambiguous. So you should add parentheses to tell VB which conditions you want it to evaluate first.

    Consider the If statement in this lesson's Quiz Data Entry program. This statement contains a complex, three-part condition.

    Here are the three conditions:

    When you have more than two conditions like this, group related conditions with parentheses. Otherwise, you can introduce a bug into your program.

    What bug? This statement is ambiguous. It can be interpreted two ways:

    • That the txtAnswer field must be either t or f—and that the txtQuestion must have some text in it. (This is what we mean.)
    • That if the txtAnswer field holds a t, everything is valid. (This is not what we intended.)

    See if you can figure out what happens if the parentheses are moved to the wrong location, like this:

    • Here you're saying that the Q/A pair is valid any time the txtAnswer field holds a t. Regardless of what's going on with the txtQuestion field, it can be empty. (This isn't what we mean at all! But that's what this code says.)

    You can see that it's important to use parentheses to let VB know which conditions it should evaluate first.

    The same problem arises with math calculations: If you have more than one calculation in a statement (such as division and subtraction), it's totally ambiguous. Consider this problem:

    10 / 5 – 1

    Did you get 2.5? Or 1?

    We can interpret this calculation two different ways:

    • First divide 10 by 5, and then subtract 1.

    OR

    • First subtract 1 from 5, and then divide 10 by the result.

    So you must add parentheses to tell VB that you want the (enclosed) calculation done first.

    (10 / 5) - 1 results in the answer 1

    10 / (5 - 1) results in 2.5

    Assignment

    Data entry is repetitive by nature, but you can try to make it as painless as possible by having your program assume some of the burden. Look for things the computer can do instead of the typist.

    In the Quiz Data Entry program, each time the user types a question, he or she has to add a question mark. But the program can do that for the user! Just have the program add a question mark when the user finishes typing a question into the textbox field and presses the TAB key to move to the answer field.

    Add this Leave event to the program:



    How did you do? The code above detects whether the user has typed a question mark—and if not, it adds one.